/*
    日志模块
        1、提供三种日志级别的宏函数
        2、支持日志输出到标准输出流和文件
*/
#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <cstdarg>
#include <mutex>

#define LOG(level, format, ...)                                       \
    do                                                                \
    {                                                                 \
        logMessage(__FILE__, __LINE__, level, format, ##__VA_ARGS__); \
    } while (0)
#define LOG_DEBUG(format, ...) LOG(Level::DEBUG, format, ##__VA_ARGS__)
#define LOG_INFO(format, ...) LOG(Level::INFO, format, ##__VA_ARGS__)
#define LOG_ERROR(format, ...) LOG(Level::ERROR, format, ##__VA_ARGS__)

// 日志等级
enum Level
{
    DEBUG = 0,
    INFO,
    ERROR
};
int DEFAULT_LOG_LEVEL = Level::DEBUG; // 默认日志等级
std::mutex mtx;                       // 全局锁：保护日志输出
const std::string logFile = "log.log";   // 日志文件
bool IsSaveToFile = false;            // 全局变量，是否将日志保存到文件

// 设置日志默认等级
#define SetDefaultLogLevel(level)  \
    do                             \
    {                              \
        DEFAULT_LOG_LEVEL = level; \
    } while (0)

// 定义两个使能按钮
#define EnableSaveToScreen()  \
    do                        \
    {                         \
        IsSaveToFile = false; \
    } while (0)

#define EnableSaveFile()     \
    do                       \
    {                        \
        IsSaveToFile = true; \
    } while (0)

// 获取时间字符串
std::string getTimeStr()
{
    time_t t = time(NULL);
    struct tm *ptm = localtime(&t);
    char buf[1024];
    sprintf(buf, "%d-%02d-%02d %02d:%02d:%02d", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
    return buf;
}

// 将日志保存到文件
void saveToFile(const std::string &file, std::string &msg)
{
    std::ofstream out(file.c_str(), std::ios::app);
    if (!out.is_open())
        return;
    out << msg;
    out.close();
}

// 日志输出
void logMessage(std::string file, int line, Level level, std::string format, ...)
{
    if (level >= DEFAULT_LOG_LEVEL)
    {
        // 获取事件字符串
        std::string timeStr = getTimeStr();

        // 将可变形参安装format格式化输出到buf中
        va_list val;
        va_start(val, format);
        char buf[1024];
        vsprintf(buf, format.c_str(), val);

        // 构建日志字符串
        std::string msg = "[" + timeStr + "]" + "[" + file + ":" + std::to_string(line) + "] " + buf;

        // 输出日志
        std::lock_guard<std::mutex> lock(mtx);
        if (IsSaveToFile)
        {
            saveToFile(logFile, msg); // 输出到文件
        }
        else
        {
            std::cout << msg; // 输出到标准输出流
        }
    }
}